在 Rust 中,結構體 (Struct) 讓我們可以將相關數據組織在一起。但光有數據還不夠,我們通常還希望能夠對這些數據執行操作。這時,「方法」(Methods) 就登場了。方法是隸屬於特定實例的函式,它們可以存取或修改該實例的數據。
要為一個結構體定義方法,我們需要使用 impl 關鍵字,它代表「實作」(implementation)。impl 區塊的格式如下:
impl StructName {
    // 方法定義
    fn method_name(parameter1: Type1, ...) -> ReturnType {
        // 方法的程式碼
    }
}
impl StructName:表示我們接下來要為名為 StructName 的結構體實作方法。impl 區塊內部,我們可以定義一個或多個方法。方法的定義與一般函式非常相似。#[derive(Debug)] // 這是屬性,讓結構體可以用 {:?} 印出,稍後會用到
struct TaylorSwiftSong {
    title: String,
    year: u32,
    duration_secs: u32,
}
// 使用 impl 為 TaylorSwiftSong 結構體實作方法
impl TaylorSwiftSong {
    // 定義一個名為 display_song_info 的方法
    // 它的第一個參數是 self
    fn display_song_info(self) {
        println!("Title: {}", self.title);
        println!("Release year: {}", self.year);
        println!("Duration: {}sec.", self.duration_secs);
    }
}
fn main() {
    // 創建 TaylorSwiftSong 結構體的一個實例
    let song = TaylorSwiftSong {
        title: String::from("Black Space"), // 原曲名應為 "Blank Space" 😉
        year: 2014,
        duration_secs: 231,
    };
    // 呼叫實例上的方法
    // 語法:instance.method_name()
    song.display_song_info();
}
struct TaylorSwiftSong { ... }:
TaylorSwiftSong 的結構體,它有三個欄位:title (歌曲名稱,String 型別),year (發行年份,u32 型別),以及 duration_secs (歌曲時長,秒,u32 型別)。#[derive(Debug)] 是一個屬性,它讓編譯器自動為我們的結構體產生 Debug Trait 的實作,這樣我們就可以使用 {:?} 或 {:#?} 來印出結構體的內容(雖然在這個範例中沒有直接這樣印出整個 song,但這是個好習慣)。impl TaylorSwiftSong { ... }:
TaylorSwiftSong 結構體定義方法。fn display_song_info(self) { ... }:
display_song_info。self,它代表呼叫該方法的結構體實例本身。println! 來印出歌曲的標題、年份和時長。我們透過 self.title、self.year 和 self.duration_secs 來存取實例的欄位。fn main() { ... }:
main 函式中,我們創建了一個 TaylorSwiftSong 的實例,名為 song,並初始化其欄位。song.display_song_info();:這是呼叫方法的方式。我們使用點號 (.) 將實例 song 與其方法 display_song_info 連接起來。 (如筆記所述:"value.method(); value.method(3, "apples")")在 Rust 中,方法的第一個參數 self 可以有幾種不同的形式,這會影響到方法如何與實例互動,特別是關於所有權和可變性,根據前述程式碼略做修改,針對參數變化做說明。
#[derive(Debug)]
struct TaylorSwiftSong {
    title: String,
    year: u32,
    duration_secs: u32,
}
impl TaylorSwiftSong {
    fn display_song_info(&self) {
        println!("Title: {}", self.title);
        println!("Release year: {}", self.year);
        println!("Duration: {}sec.", self.duration_secs);
    }
    fn double_secs(&mut self) {
        self.duration_secs = self.duration_secs * 2;
        println!("Duration: {}sec.", self.duration_secs);
    }
}
fn main() {
    let mut song = TaylorSwiftSong {
        title: String::from("Black Space"),
        year: 2014,
        duration_secs: 231,
    };
    song.display_song_info();
    song.double_secs();
}
self (或 self: Self 或 self: TaylorSwiftSong) - 取得所有權
self,或者明確寫出型別 self: Self (其中 Self 是 impl 區塊所針對的型別,即 TaylorSwiftSong),或者 self: TaylorSwiftSong。這三種在這種情況下是等效的。self 時,呼叫該方法會將實例的所有權移動 (move) 到方法中。一旦方法執行完畢,如果這個實例沒有被回傳,它就會被銷毀 (drop)。display_song_info(self) 就是這種情況。這意味著當 song.display_song_info(); 執行後,song 實例的所有權就被轉移給了 display_song_info 方法。因為 display_song_info 沒有回傳任何東西,所以 song 在方法結束後就被銷毀了。如果我們之後再嘗試使用 song (例如 println!("{:?}", song);),就會遇到編譯錯誤。mut self (或 self: mut Self 或 self: mut TaylorSwiftSong) - 取得所有權並使其在方法內部可變
mut self,或者明確寫出型別 self: mut Self 或 self: mut TaylorSwiftSong。self 形式一樣,當方法的第一個參數是 mut self 時,呼叫該方法會將實例的所有權移動 (move) 到方法中。這意味著呼叫者在方法呼叫後(除非方法將其回傳)就失去了對原始實例的所有權。mut 關鍵字在這裡表示,在方法的作用域內部,這個名為 self 的綁定是可變的。因此,方法可以修改這個它現在擁有的實例的欄位。mut self 允許在轉換(或消耗)前進行修改。// 繼續 impl TaylorSwiftSong { ... }
fn log_and_consume_as_remix(mut self) -> String {
    // 因為是 mut self,我們可以修改 self 的欄位
    self.title.push_str(" (Remix)"); // 修改了 self 擁有的 title
    let log_entry = format!(
        "[LOG] Consumed Remix Song: Title: {}, Year: {}, Duration: {}sec.",
        self.title, self.year, self.duration_secs
    );
    // self 的所有權在此結束,它會在這裡被 drop (除非被回傳)
    log_entry // 回傳格式化後的字串
}
// } // 結束 impl TaylorSwiftSong
因此這樣呼叫就會:
   let song = TaylorSwiftSong { title: String::from("Style"), year: 2014, duration_secs: 230 };
   let log_message = song.log_and_consume_as_remix();
   println!("{}", log_message);
// // ❌ 此時 song 變數已失效,不能再使用
&self (或 self: &Self 或 self: &TaylorSwiftSong) - 不可變借用
display_song_info 的目的是僅顯示資訊而不消耗歌曲實例,那麼 &self 會是更常見的選擇。   impl TaylorSwiftSong {
        fn display_song_info(&self) { // 注意這裡的 &self
           println!("Title: {}", self.title);
              ...
       }
   }
&mut self (或 self: &mut Self 或 self: &mut TaylorSwiftSong) - 可變借用
song.add_to_playlist() 或 song.update_duration(new_duration)。